#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/irq.h>
#include <linux/poll.h>
#include <linux/timer.h>
#include <linux/input.h>
#include <linux/platform_device.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/arch/regs-gpio.h>

#define KEYS_NUMBER 4

//去抖使用的定时器
static struct timer_list keys_timer;

//输入子系统
static struct input_dev *keys_dev = NULL;

//io状态获取使用
struct ping_desc
{
    int irq;
    char *name;
    unsigned int pin;
    unsigned int key_val; //按键的返回值
};

//发生中断时的引脚描述符
struct ping_desc *irq_pd = NULL;

struct ping_desc pings_desc[4] = {
    {IRQ_EINT0,  "key1", S3C2410_GPF0,  KEY_L},
    {IRQ_EINT2,  "key2", S3C2410_GPF2,  KEY_S},
    {IRQ_EINT11, "key3", S3C2410_GPG3,  KEY_ENTER},
    {IRQ_EINT19, "key4", S3C2410_GPG11, KEY_RIGHTSHIFT},
};

/*
 * irq 中断号
 * dev_id 注册驱动程序时传入的
 */
static irqreturn_t key_irq(int irq, void *dev_id)
{
    irq_pd = (struct ping_desc *)dev_id;

    // 修改定时器的超时时间
    // jiffies 为系统滴答时钟时间
    // 10ms 后执行， HZ / 100
    mod_timer(&keys_timer, jiffies+(HZ/100));

    return IRQ_RETVAL(IRQ_HANDLED);
}

//定时器相关函数
static void keys_timer_function(unsigned long data)
{
    struct ping_desc *pin = irq_pd;
    unsigned int pinval = 0;

    //如果没有按键产生
    if(!pin)
        return;

    pinval = s3c2410_gpio_getpin(pin->pin);
    if (pinval)
    {
        input_event(keys_dev, EV_KEY, pin->key_val, 0);
        input_sync(keys_dev);
    }
    else
    {
        input_event(keys_dev, EV_KEY, pin->key_val, 1);
        input_sync(keys_dev);
    }
}

int keys_init(void)
{
    int i = 0;

    /* 1. 分配 input_dev 结构体 */
    keys_dev = input_allocate_device();

    /* 2. 设置 结构体内容 */
    set_bit(EV_KEY, keys_dev->evbit); //设置为按键类事件
    set_bit(EV_REP, keys_dev->evbit); //设置为按键类事件

    set_bit(KEY_L, keys_dev->keybit); //添加L键
    set_bit(KEY_S, keys_dev->keybit); //添加S键
    set_bit(KEY_ENTER, keys_dev->keybit); //添加回车键
    set_bit(KEY_RIGHTSHIFT, keys_dev->keybit); //添加右边的shift键

    /* 3. 注册 结构体 */
    input_register_device(keys_dev);

    /* 4. 硬件相关 */
    init_timer(&keys_timer);
    keys_timer.function = keys_timer_function;
    add_timer(&keys_timer);

    for(i=0;i<KEYS_NUMBER;i++)
    {
        // GPF0,2 GPG11,19
        // 注册中断函数
        // IRQ_EINT0 中断号
        // key_irq 处理函数
        // IRQT_BOTHEDGE 双边延触发
        // "key1" 驱动名称
        // 驱动ID，进入中断时会传入,我们传入相应的结构体指针地址
        request_irq(pings_desc[i].irq, key_irq, IRQT_BOTHEDGE,  pings_desc[i].name, &pings_desc[i]);
    }

    return 0;
}

void keys_exit(void)
{
    int i = 0;
    // 删除已经注册的中断
    for(i=0;i<KEYS_NUMBER;i++)
    {
        free_irq(pings_desc[i].irq, &pings_desc[i]);
    }

    //删除定时器
    del_timer(&keys_timer);
    //取消注册的结构体
    input_unregister_device(keys_dev);
    //释放结构体相关内存空间
    input_free_device(keys_dev);
}

module_init(keys_init); //添加入口函数
module_exit(keys_exit); //删除入口函数

MODULE_LICENSE("GPL");
